﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace FCS
{
    #region Head<T>

    sealed class Head<T> : FunList<T>, IFunValue<T>
    {
        private IFunList<T> List { get; set; }

        public Head(IFunList<T> list)
        {
            List = list;
        }

        public T Value
        {
            get
            {
                return List.First();
            }
        }

        #region IEnumerable<T> Members

        public override IEnumerator<T> GetEnumerator()
        {
            foreach (var v in List)
            {
                yield return Value;
                break;
            }
        }

        #endregion
    }

    #endregion

    #region Tail<T>

    sealed class Tail<T> : FunList<T>
    {
        private IFunList<T> List { get; set; }

        public Tail(IFunList<T> list)
        {
            List = list;
        }

        #region IEnumerable<T> Members

        public override IEnumerator<T> GetEnumerator()
        {
            bool first = true;
            foreach (T val in List)
            {
                if (!first)
                {
                    yield return val;
                }
                first = false;
            }
        }

        #endregion
    }

    #endregion

    #region Cons<T>

    sealed class Cons<T> : FunList<T>
    {
        private T First { get; set; }
        private IFunList<T> Tail { get; set; }

        public Cons(T first, IFunList<T> tail)
        {
            First = first;
            Tail = tail;
        }

        #region IEnumerable<T> Members

        public override IEnumerator<T> GetEnumerator()
        {
            yield return First;
            if (Tail != null)
            {
                foreach (T val in Tail)
                {
                    yield return val;
                }
            }
        }

        #endregion
    }

    #endregion

    #region ConsL<T>

    sealed class ConsL<T> : FunList<IFunList<T>>
    {
        private IFunList<T> Firsts { get; set; }
        private IFunList<T> Tail { get; set; }
        private Persist<T> pTail;

        public ConsL(IFunList<T> firsts, IFunList<T> tail)
        {
            Firsts = firsts;
            Tail = tail;
            pTail = null;
        }

        #region IEnumerable<IFunList<T>> Members

        public override IEnumerator<IFunList<T>> GetEnumerator()
        {
            foreach (var v in Firsts)
            {
                yield return GenLst(v);
            }
        }

        private IFunList<T> GenLst(T v)
        {
            if (pTail == null)
            {
                pTail = new Persist<T>(Tail);
            }
            return new Cons<T>(v, pTail);
        }

        #endregion
    }

    #endregion

    #region ToList<T>

    sealed class ToList<T> : FunList<T>
    {
        private T First { get; set; }

        public ToList(T first)
        {
            First = first;
        }

        #region IEnumerable<T> Members

        public override IEnumerator<T> GetEnumerator()
        {
            yield return First;
        }

        #endregion
    }

    #endregion

    #region Compose<T>

    sealed class Compose<T> : FunList<T>
    {
        private IFunList<T> First { get; set; }
        private IFunList<T> Second { get; set; }
        private bool HasList;
        private T SecondVal { get; set; }
        private bool HasVal;

        public Compose(IFunList<T> first, IFunList<T> second)
        {
            First = first;
            Second = second;
            HasList = true;
            HasVal = false;
        }

        public Compose(IFunList<T> first, T second)
        {
            First = first;
            SecondVal = second;
            HasList = false;
            HasVal = true;
        }

        #region IEnumerable<T> Members

        public override IEnumerator<T> GetEnumerator()
        {
            if (First != null)
            {
                foreach (T val in First)
                {
                    yield return val;
                }
            }
            if (HasList && Second != null)
            {
                foreach (T val in Second)
                {
                    yield return val;
                }
            }
            else if(HasVal)
            {
                yield return SecondVal;
            }
        }

        #endregion
    }

    #endregion

    #region IsEmpty<T>

    sealed class IsEmpty<T> : IFunValue<bool>
    {
        private IFunList<T> List { get; set; }

        public IsEmpty(IFunList<T> list)
        {
            List = list;
        }

        public bool Value
        {
            get
            {
                return !List.GetEnumerator().MoveNext();
            }
        }
    }

    #endregion

    #region Take<T>

    sealed class Take<T> : FunList<T>
    {
        private IFunList<T> List { get; set; }
        private int Count { get; set; }

        public Take(int count, IFunList<T> list)
        {
            List = list;
            Count = count;
        }

        #region IEnumerable<T> Members

        public override IEnumerator<T> GetEnumerator()
        {
            return List.Take(Count).GetEnumerator();
        }

        #endregion
    }

    #endregion

    #region At<T>

    sealed class At<T> : IFunValue<T>
    {
        private IFunList<T> List { get; set; }
        private int Idx { get; set; }

        public At(int idx, IFunList<T> list)
        {
            List = list;
            Idx = idx;
        }

        public T Value
        {
            get
            {
                return List.ElementAt(Idx);
            }
        }
    }

    #endregion
}
